package com.ayz.gateway.common.notify;




import com.ayz.gateway.common.notify.exception.EventTypeNotFound;
import net.sf.cglib.proxy.Enhancer;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 当前广播器中注册的监听器不能重复
 * 即理论上来说不允许同一个listener类有多个实例
 */
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster{
    //存放当前广播器中的监听器集合
    private final HashSet<ApplicationListener<?>> listeners=new HashSet<>();
    //加速
    private final Map<EventCacheKey,HashSet<ApplicationListener<?>>> eventListenerCache=new ConcurrentHashMap<>();

    //读写锁
    private final ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();

    public AbstractApplicationEventMulticaster(){
    }

    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        ReentrantReadWriteLock.WriteLock writeLock = this.readWriteLock.writeLock();
        writeLock.lock();
        try {
            this.listeners.add(listener);
            eventListenerCache.clear();
        }finally {
            if(writeLock.isHeldByCurrentThread()){
                writeLock.unlock();
            }
        }
    }

    @Override
    public void removeAllListener() {
        ReentrantReadWriteLock.WriteLock writeLock = this.readWriteLock.writeLock();
        writeLock.lock();
        try {
            this.listeners.clear();
            this.eventListenerCache.clear();
        }finally {
            if(writeLock.isHeldByCurrentThread()){
                writeLock.unlock();
            }
        }
    }

    @Override
    public void removeApplicationListener(ApplicationListener<?> listener) {
        ReentrantReadWriteLock.WriteLock writeLock = this.readWriteLock.writeLock();
        writeLock.lock();
        try {
            this.listeners.remove(listener);
            this.eventListenerCache.clear();
        }finally {
            if(writeLock.isHeldByCurrentThread()){
                writeLock.unlock();
            }
        }
    }

    /**
     * 功能:从当前广播器上注册的所有listener当中筛选出
     * 能够匹配当前事件的listener
     * @param event
     * @param eventType
     * @return
     */
    protected HashSet<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event,Class<?> eventType){
        Class<?> sourceType = event.getSource().getClass();
        EventCacheKey cacheKey=new EventCacheKey(eventType,sourceType);
        //1:先从缓存当中获取
        HashSet<ApplicationListener<?>> result;
        result= this.eventListenerCache.get(cacheKey);
        if(result!=null && !result.isEmpty()){
            return result;
        }
        //2: 缓存中获取不到,遍历所有的listener监听器 查看是否支持处理当前事件

        HashSet<ApplicationListener<?>> applicationListeners = this.listeners;
        HashSet<ApplicationListener<?>> cachedListeners=new HashSet<>();
        if(result==null){
            result=new HashSet<>();
        }
        for (ApplicationListener<?> applicationListener : applicationListeners) {
            if(canSupport(applicationListener,eventType,sourceType)){
                result.add(applicationListener);
                cachedListeners.add(applicationListener);
            }
        }
        this.eventListenerCache.put(cacheKey,cachedListeners);
        return result;
    }

    /**
     * 判断当前applicationListener是否支持处理 event事件
     * @param applicationListener
     * @param eventType
     * @param sourceType
     * @return
     */
    protected boolean canSupport(ApplicationListener<?> applicationListener, Class<?> eventType, Class<?> sourceType) {
        if(applicationListener instanceof GenericApplicationListener){
            //如果实现类GenericApplicationListener 当前监听器支持判断是否 适用一个事件的功能
            GenericApplicationListener gal=(GenericApplicationListener) applicationListener;
            return gal.supportsEventType(eventType)&&gal.supportsSourceType(sourceType);
        }else{
            //首先我们的事件监听器是泛型类 其泛型必须是继承ApplicationEvent的子实现类
            //所以如果当前监听器不属于 GenericApplicationListener
            //我们就通过反射获取当前监听器的泛型 看看这个泛型是不是当前 事件类或者是其的父类
            Class<?> targetClass= Enhancer.isEnhanced(applicationListener.getClass())?applicationListener.getClass().getSuperclass():
                    applicationListener.getClass();

            Type genericInterface = targetClass.getGenericInterfaces()[0];

            Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
            String className = actualTypeArgument.getTypeName();
            Class<?> eventClass;//当前监听器 的泛型事件的Class对象
            try {
                eventClass=Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new EventTypeNotFound("Wrong event class name :"+className,e);
            }
            return eventClass.isAssignableFrom(eventType);
        }
    }
    public HashSet<ApplicationListener<?>> getListeners() {
        return listeners;
    }

    public Map<EventCacheKey, HashSet<ApplicationListener<?>>> getEventListenerCache() {
        return eventListenerCache;
    }

    public ReentrantReadWriteLock getReadWriteLock() {
        return readWriteLock;
    }

    private static class EventCacheKey {
        private Class<?> eventType;//事件类型
        private Class<?> sourceType;//事件源类型

        public EventCacheKey(Class<?> eventType, Class<?> sourceType) {
            this.eventType = eventType;
            this.sourceType = sourceType;
        }

        public Class<?> getEventType() {
            return eventType;
        }

        public void setEventType(Class<?> eventType) {
            this.eventType = eventType;
        }

        public Class<?> getSourceType() {
            return sourceType;
        }

        public void setSourceType(Class<?> sourceType) {
            this.sourceType = sourceType;
        }



        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            EventCacheKey that = (EventCacheKey) o;
            return Objects.equals(eventType, that.eventType) && Objects.equals(sourceType, that.sourceType);
        }

        @Override
        public int hashCode() {
            return Objects.hash(eventType, sourceType);
        }
    }


}
